package com.wpx.log.core;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import com.wpx.log.core.annotation.OpLogHandler;
import com.wpx.log.core.exception.InitException;
import com.wpx.log.core.handler.ILogHandler;
import com.wpx.log.core.handler.desc.ILogDescPolicy;
import com.wpx.log.core.handler.field.equals.IFieldEqualsPolicy;
import com.wpx.log.core.handler.field.equals.VoidFieldEqualsPolicy;
import com.wpx.log.core.handler.field.render.IFieldRenderPolicy;
import com.wpx.log.core.handler.field.render.VoidFieldRenderPolicy;
import com.wpx.log.core.handler.param.ILogParamTypePolicy;
import com.wpx.log.core.handler.status.ILogStatusPolicy;
import com.wpx.log.core.module.DefaultLog;
import com.wpx.log.core.module.Entry;
import com.wpx.log.core.service.DefaultLogService;
import com.wpx.log.core.service.IWpxLogService;
import com.wpx.log.spring.boot.autoconfigure.LogProperties;
import com.wpx.log.util.Utils;


public class LogContext implements InitializingBean{

	private static final Logger logger = LoggerFactory.getLogger(LogContext.class);
			
	private Class<?> logClass;
	
	private List<Entry<Field, ILogHandler>> logHandlers = new ArrayList<Entry<Field,ILogHandler>>();
	
	private Map<Class<?>, IFieldEqualsPolicy> fieldEqualsPolicys = new HashMap<Class<?>, IFieldEqualsPolicy>();
	
	private Map<Class<?>, IFieldRenderPolicy> fieldRenderPolicys = new HashMap<Class<?>, IFieldRenderPolicy>();
	
	private Map<Class<? extends ILogDescPolicy>, ILogDescPolicy> logDescPolicys = new HashMap<Class<? extends ILogDescPolicy>, ILogDescPolicy>();
	
	private Map<Class<?>, ILogStatusPolicy> logStatusPolicys = new HashMap<Class<?>, ILogStatusPolicy>();
	
	private Map<String, ILogParamTypePolicy> paramTypePolicys = new HashMap<String, ILogParamTypePolicy>();
	
	private Map<String,List<String>> ipWhiteMap;
	
	private Map<String,List<String>> ipBlackMap;
	
	private Map<String, Object> attributes = new HashMap<String, Object>();
	
	private boolean debug;
	
	@Autowired(required = false)
	private IWpxLogService logservice;
	
	private LogProperties properties;
	
	public LogContext(LogProperties properties) {
		this.properties = properties;
		initLogClass();
		initLogHandler();
		this.ipWhiteMap = initIpMap(properties.getWhiteList());
		this.debug = properties.isDebug();
		if(this.debug) {
			logger.info("*****************wpxLog******************");
		}
		this.ipBlackMap = initIpMap(properties.getBlackList());
	}

	
	
	private Map<String,List<String>> initIpMap(Map<String,List<String>> ipMap) {
		if(this.debug) {
			logger.info("初始化ip黑白名单:");
		}
		if(ipMap == null){
			ipMap = new HashMap<String, List<String>>();
		}
		//检查ip合法性
		ipMap.forEach((g, ips) -> {
			ips.forEach(ip -> {
				if(Utils.checkIp(ip) == false) {
					throw new InitException("ip="+ip+" ,不合法");
				}
			});
		});		
		
		if(ipMap.get("root") == null) {
			ipMap.put("root", new ArrayList<String>());
		}
		
		
		if(this.debug) {
			ipMap.forEach((g, ips) -> {
				logger.info("group={},ips={}", g, ips);
			});
			
		}
		return ipMap;
	}

	@Autowired
	public void setParamTypePolicys(List<ILogParamTypePolicy> policys) {
		if(this.debug) {
			logger.info("初始化paramTypePolicys 参数解析策略:");
		}
		if(policys == null) return;
		for (ILogParamTypePolicy policy : policys) {
			this.paramTypePolicys.put(policy.type(), policy);
		}
		if(this.debug) {
			policys.stream().distinct().forEach(policy ->{
				logger.info("paramTypePolicy={}" , policy.getClass().getName());
			});
		}
	}

	@Autowired
	public void setFieldEqualsPolicys(List<IFieldEqualsPolicy> policys) {
		if(this.debug) {
			logger.info("初始化FieldEqualsPolicys 属性比较策略:");
		}
		if(policys == null) return;
		for (IFieldEqualsPolicy policy : policys) {
			this.fieldEqualsPolicys.put(policy.getType(), policy);
			this.fieldEqualsPolicys.put(policy.getClass(), policy);
		}
		if(this.debug) {
			policys.stream().distinct().forEach(policy ->{
				logger.info("FieldEqualsPolicy={}" , policy.getClass().getName());
			});
		}
	}
	
	@Autowired
	public void setFieldRenderPolicys(List<IFieldRenderPolicy> policys) {
		if(this.debug) {
			logger.info("初始化FieldRenderPolicys 属性渲染策略:");
		}
		if(policys == null) return;
		for (IFieldRenderPolicy policy : policys) {
			this.fieldRenderPolicys.put(policy.getType(), policy);
			this.fieldRenderPolicys.put(policy.getClass(), policy);
		}
		if(this.debug) {
			policys.stream().distinct().forEach(policy ->{
				logger.info("fieldRenderPolicy={}" , policy.getClass().getName());
			});
		}
	}
	
	@Autowired
	public void setLogDescPolicys(List<ILogDescPolicy> policys) {
		if(this.debug) {
			logger.info("初始化LogDescPolicys 描述策略:");
		}
		if(policys == null) return;
		for (ILogDescPolicy policy : policys) {
			this.logDescPolicys.put(policy.getClass(), policy);
		}
		if(this.debug) {
			policys.stream().distinct().forEach(policy ->{
				logger.info("logDescPolicys={}" , policy.getClass().getName());
			});
		}
	}
	
	@Autowired(required = false)
	public void setLogStatusPolicys(List<ILogStatusPolicy> policys) {
		if(this.debug) {
			logger.info("初始化LogStatusPolicys 状态策略:");
		}
		if(policys == null) return;
		for (ILogStatusPolicy policy : policys) {
			this.logStatusPolicys.put(policy.getType(), policy);
		}

		if(this.debug) {
			policys.stream().distinct().forEach(policy ->{
				logger.info("logStatusPolicys={}" , policy.getClass().getName());
			});
		}
	}
	
	public IFieldEqualsPolicy getFieldEqualsPolicy(Class<? > clazz) {
		if(clazz == VoidFieldEqualsPolicy.class) return null;
		return this.fieldEqualsPolicys.get(clazz);
	}
	
	public IFieldRenderPolicy getFieldRenderPolicy(Class<?> clazz) {
		if(clazz == VoidFieldRenderPolicy.class) return null;
		return this.fieldRenderPolicys.get(clazz);
	}
	
	public ILogDescPolicy getLogDescPolicy(Class<? extends ILogDescPolicy> clazz) {
		return this.logDescPolicys.get(clazz);
	}
	
	
	public ILogStatusPolicy getLogStatusPolicy(Class<?> clazz) {
		return this.logStatusPolicys.get(clazz);
	}
	
	public ILogParamTypePolicy getLogParamTypePolicy(String centextType) {
		return this.paramTypePolicys.get(centextType);
	}
	
	void initLogClass() {
		String entityClass = this.properties.getEntityClass();
		try {
			if(entityClass == null) {
				this.logClass = DefaultLog.class;
			}else {
				this.logClass = Class.forName(entityClass);
			}
			
			if(this.debug) {
				logger.info("Log 实体类={}", this.logClass.getName());
			}
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("找不到log类 " + entityClass);
		}
	}
	void initLogHandler() {
		if(this.logClass == null) {
			throw new RuntimeException("没有指定日志实体类!");
		}
		if(this.debug) {
			logger.info("初始化LogHandler:");
		}
		try {
			Field[] fields = Utils.getMemberField(logClass);
			for (Field field : fields) {
				OpLogHandler logHandler = field.getAnnotation(OpLogHandler.class);
				if(logHandler != null) {
					if(this.debug) {
						logger.info("LogHandler={}", logHandler.value().getName());
					}
					ILogHandler newInstance = logHandler.value().newInstance();
					field.setAccessible(true);
					this.logHandlers.add(new Entry<Field, ILogHandler>(field, newInstance));
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("初始化LogHandler失败!", e);
		}
		
	}
	
	public boolean isWhiteIp(String group, String ip) {
		if(this.ipWhiteMap.keySet().size() == 1 && this.ipWhiteMap.get("root").size() == 0) {
			return true;
		}
		return matchIp(this.ipWhiteMap, group, ip);
	}
	
	public boolean isBlackIp(String group, String ip) {
		return matchIp(this.ipBlackMap, group, ip);
	}
	
	private boolean matchIp(Map<String, List<String>> ipWhiteMap, String group, String ip) {
			
		if(StringUtils.isEmpty(group) || StringUtils.isEmpty(ip)) {
			return true;
		}
		
		List<String> rootIps = ipWhiteMap.get("root");
		for (String rootIp : rootIps) {
			if(ip.equals(rootIp)) return true;
		}
		Set<String> keySet = ipWhiteMap.keySet();
		for (String g : keySet) {
			if(group.indexOf(g) == 0) {
				List<String> ips = ipWhiteMap.get(g);
				for (String gIp : ips) {
					if(ip.equals(gIp)) return true;
				}
			}
		}
				
		return false;
	}



	public Class<?> getLogClass() {
		return logClass;
	}

	public void setLogClass(Class<?> logClass) {
		this.logClass = logClass;
	}

	public IWpxLogService getLogservice() {
		return logservice;
	}

	public void setLogservice(IWpxLogService logservice) {
		this.logservice = logservice;
	}

	public List<Entry<Field, ILogHandler>> getLogHandlers(boolean isSync) {
		return logHandlers.stream().filter(emtry -> emtry.getValue().isSync() == isSync).collect(Collectors.toList());
	}

	public void setAttribute(String name, Object value) {
		this.attributes.put(name, value);
	}
	
	public Object getAttribute(String name) {
		return this.attributes.get(name);
	}
	
	public LogProperties getProperties() {
		return properties;
	}

	public void setProperties(LogProperties properties) {
		this.properties = properties;
	}

	public boolean isDebug() {
		return this.debug;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if(this.logservice == null) {
			this.logservice = new DefaultLogService();
		}
		if(this.debug) {
			logger.info("*****************wpxLog******************");
		}
	}
	
	
}
